<?php

/**
 * 数据库适配器
 * 定义通用的数据库适配接口
 *
 * @author edikud
 * @package Db
 * @copyright Copyright (c) 2019 Cola (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
abstract class Db_Abstract
{
    /** 数据库关键字 */
    const KEYWORDS = '*PRIMARY|AND|OR|LIKE|BINARY|BY|DISTINCT|AS|IN|IS|NULL';
	
    /**
     * 前缀
     *
     * @access private
     * @var string
     */
    private $_prefix;	

    public function __construct($prefix)
    {
        $this->_prefix = $prefix;
    }
	
    /**
     * 过滤表前缀,表前缀由table.构成
     *
     * @param string $string 需要解析的字符串
     * @return string
     */
    public function filterPrefix($string)
    {
		return false !== strpos($string, 'table.') ? str_replace('table.', $this->_prefix, $string) : $string;
    }
	
    /**
     * 过滤数组键值
     *
     * @access private
     * @param string $str 待处理字段值
     * @return string
     */
    private function filterColumn($str)
    {
        $str = $str . ' 0';
        $length = strlen($str);
        $lastIsAlnum = false;
        $result = '';
        $word = '';
        $split = '';
        $quotes = 0;

        for ($i = 0; $i < $length; $i ++) {
            $cha = $str[$i];
            if (ctype_alnum($cha) || false !== strpos('_*', $cha)) {
                if (!$lastIsAlnum) {
                    if ($quotes > 0 && !ctype_digit($word) && '.' != $split
                    && false === strpos(self::KEYWORDS, strtoupper($word))) {
                        $word = $this->quoteColumn($word);
                    } else if ('.' == $split && 'table' == $word) {
                        $word = $this->_prefix;
                        $split = '';
                    }

                    $result .= $word . $split;
                    $word = '';
                    $quotes = 0;
                }

                $word .= $cha;
                $lastIsAlnum = true;
            } else {

                if ($lastIsAlnum) {
                    if (0 == $quotes) {
                        if (false !== strpos(' ,)=<>.+-*/', $cha)) {
                            $quotes = 1;
                        } else if ('(' == $cha) {
                            $quotes = -1;
                        }
                    }

                    $split = '';
                }

                $split .= $cha;
                $lastIsAlnum = false;
            }
        }

        return $result;
    }
	
	
    /**
     * 判断适配器是否可用
     *
     * @access public
     * @return boolean
     */
    public static function isAvailable(){}

    /**
     * 数据库连接函数
     *
     * @param Container $config 数据库配置
     * @return resource
     */
    public function connect(Container $config){}

    /**
     * 获取数据库版本 
     * 
     * @param mixed $handle
     * @return string
     */
    public function version($handle){}

    /**
     * 清空数据表
     *
     * @param mixed $handle 连接对象
     * @return mixed
     */
    public function truncate($handle){}
    /**
     * quoteColumn  
     * 
     * @param mixed $column 
     * @abstract
     * @access public
     * @return string
     */
    public function quoteColumn($column){}

    /**
     * quoteValue  
     * 
     * @param mixed $value 
     * @abstract
     * @access public
     * @return string
     */
    public function quoteValue($value){}

    /**
     * 合成sql语句
     *
     * @access public
     * @param array $query 查询对象词法数组
     * @return array
     */
    public function parseSql($query)
    {
        $sql = '';
        $parameter = array();
        if ($items = $query->getQuery('sql')) {
            foreach ($items as $args) 
			{
				$str = array_shift($args);
				$args && $parameter = array_merge($parameter, is_array($args[0]) ? $args[0] : $args);
                $sql .= ' ' . $str;
            }
			$sql = $this->filterPrefix($sql);
        }

		return array($sql, $parameter);
	}
	
    /**
     * 合成查询语句
     *
     * @access public
     * @param array $query 查询对象词法数组
     * @return array
     */
    public function parseSelect($query)
    {
        $table = $this->filterPrefix($query->getQuery('table'));
		$parameter = array();
		$columns = '*';
		if ($items = $query->getQuery('columns')) {
			$columns = array();
            foreach ($items as $value) 
			{
                if (is_array($value)) {
                    foreach ($value as $key => $val) 
					{
                        $columns[] = $key . ' AS ' . $val;
                    }
                } else {
                    $columns[] = $value;
                }
            }

            $columns = $this->filterColumn(implode(' , ', $columns));
		}

        if ($join = $query->getQuery('join')) {
            foreach ($join as $value) 
			{
                list($_table, $condition, $op) = $value;
				$_table = $this->filterPrefix($_table);
				$condition = $this->filterColumn($condition);
                $table = "{$table} {$op} JOIN {$_table} ON {$condition}";
            }
        }

        $where = '';
        if ($items = $query->getQuery('where')) {
            foreach ($items as $value) 
			{
                list($op, $args) = $value;
				$sql = array_shift($args);
				$args && $parameter = array_merge($parameter, is_array($args[0]) ? $args[0] : $args);
                $where .= ('' === $where ? ' WHERE' : " {$op} ") . ' (' . $this->filterColumn($sql) . ')';
            }
        }		

        $limit = '';
        if (NULL !== $query->getQuery('limit')) {
            $limit .= ' LIMIT ' . $query->getQuery('limit');
        }

        $offset = '';
        if (NULL !== $query->getQuery('offset')) {
            $offset .= ' OFFSET ' . $query->getQuery('offset');
        }

        $group = '';
        if ($query->getQuery('group')) {
            $group .= ' GROUP BY ' . $this->filterColumn($query->getQuery('group'));
        }

        $having = '';
        if ($items = $query->getQuery('having')) {
            foreach ($items as $value) 
			{
                list($op, $args) = $value;
				$sql = array_shift($args);
				$args && $parameter = array_merge($parameter, is_array($args[0]) ? $args[0] : $args);
				$having .= ('' === $having ? ' HAVING' : " {$op} ") . ' (' . $this->filterColumn($sql) . ')';
            }
        }
		
        $order = '';
        if ($items = $query->getQuery('order')) {
            $order = array();
            foreach ($items as $val) 
			{
                list ($column, $sort) = $val;
                $order[] = $this->filterColumn($column) . $sort;
            }
            
            $order = ' ORDER BY ' . implode(', ', $order);
        }

		return array('SELECT ' . $columns .' FROM ' .$table.$where.$group.$having.$order.$limit.$offset, $parameter);
	}
	
    /**
     * 合成删除语句
     *
     * @access public
     * @param array $query 查询对象词法数组
     * @return array
     */
    public function parseDelete($query)
    {
		$table = $this->filterPrefix($query->getQuery('table'));
        $parameter = array();
        $where = '';
        if ($items = $query->getQuery('where')) {
            foreach ($items as $value) 
			{
                list($op, $args) = $value;
				$sql = array_shift($args);
				$args && $parameter = array_merge($parameter, is_array($args[0]) ? $args[0] : $args);
                $where .= ('' === $where ? ' WHERE' : " {$op} ") . ' (' . $this->filterColumn($sql) . ')';
            }
        }	
		
        $limit = '';
        if (NULL !== $query->getQuery('limit')) {
            $limit .= ' LIMIT ' . $query->getQuery('limit');
        }
		
        $offset = '';
        if (NULL !== $query->getQuery('offset')) {
            $offset .= ' OFFSET ' . $query->getQuery('offset');
        }
		
		return array('DELETE FROM ' .$table.$where.$limit.$offset, $parameter);
	}

    /**
     * 合成更新语句
     *
     * @access public
     * @param array $query 查询对象词法数组
     * @return array
     */
    public function parseUpdate($query)
    {
		$table = $this->filterPrefix($query->getQuery('table'));
        $update = array();
		$parameter = array();
        if ($items = $query->getQuery('values')) {
            foreach ($items as $key => $val) 
			{
                $update[] = $this->filterColumn($key) . '=?';
				$parameter[] = $val;
            }
        }
		
        if ($items = $query->getQuery('expression')) {
            foreach ($items as $values) 
			{
				list($key, $value, $escape) = $values;
                $update[] = $this->filterColumn($key) . '=' . ($escape ? $this->filterColumn($value) : $value);
            }
        }

        $update = $update ? ' SET ' . implode(' , ', $update) : '';

        $where = '';
        if ($items = $query->getQuery('where')) {
            foreach ($items as $value) 
			{
                list($op, $args) = $value;
				$sql = array_shift($args);
				$args && $parameter = array_merge($parameter, is_array($args[0]) ? $args[0] : $args);
                $where .= ('' === $where ? ' WHERE' : " {$op} ") . ' (' . $this->filterColumn($sql) . ')';
            }
        }		
		
		return array('UPDATE '.$table.$update.$where, $parameter);
	}

    /**
     * 合成插入语句
     *
     * @access public
     * @param array $query 查询对象词法数组
     * @return array
     */
    public function parseInsert($query)
    {
		$table = $this->filterPrefix($query->getQuery('table'));
		$parameter = array();
        $values = '';
        if ($items = $query->getQuery('values')) {

            $keys = array();
            $vals = array();
            
            foreach ($items as $key => $val) 
			{
                $keys[] = $this->filterColumn($key);
                $vals[] = '?';
				$parameter[] = $val;
            }

            $values .= ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $vals) . ')';
        }
		
        $limit = '';
        if (NULL !== $query->getQuery('limit')) {
            $limit .= ' LIMIT ' . $query->getQuery('limit');
        }
		
		return array('INSERT INTO '.$table.$values.$limit, $parameter);
	}
}